#include "config_file.h"
#include <unordered_map>
#include <regex>
const std::string log(const char* file, const char* func, int line)
{
    char buf[128];
    sprintf(buf, "[%s.%s.%d]", file, func, line);
    return std::string(buf);
}
#define __log__() log(__FILE__, __func__, __LINE__)

ConfigFile* ConfigFile::_instance = nullptr;

ConfigFile& ConfigFile::instance()
{
    if(_instance == NULL)
    {
        _instance = new ConfigFile();
    }
    return *_instance;
}

bool ConfigFile::isSection(std::string line, std::string& section)
{
    std::regex re(R"(^\s*\[\s*(\w+)\s*\]\s*$)");
    std::smatch match;
    if(std::regex_match(line, match, re))
    {
        section = match[1];
        return true;
    }
    return false;
}

unsigned ConfigFile::parseNumber(const std::string& )
{
    return 0;
}

std::string ConfigFile::trimLeft(const std::string& s)
{
    std::string::size_type i = 0;
    for(; i<s.size() && s[i] == ' '; i++);
    return std::string(s.begin() + i, s.end());
}

std::string ConfigFile::trimRight(const std::string& s)
{
    std::string::size_type n = s.size();
    std::string::size_type i = 0;
    for(; i<n && s[n-i] == ' '; i++);

    return std::string(s.begin(), s.end() - i);
}

std::stringstream& ConfigFile::operator[] (const std::string& name)
{
    // section.key
    _istr.clear(); 
    _istr.str(""); 
    std::regex re(R"(^\s*(\w+)\.(\w+)\s*$)");
    std::string k1;
    std::string k2;
    std::smatch ret;
    std::string str_val = "";
    if(std::regex_match(name, ret, re))
    {
        k1 = ret[1];
        k2 = ret[2];
        auto m = _map.find(k1);
        if(m == _map.end())
        {
            
            return _istr;
        }
        if(m->second->find(k2) == m->second->end())
        {
            return _istr;
        }
        _istr << (*_map[k1])[k2];
        return _istr;
    }

    return _istr;
}
std::string ConfigFile::trim(const std::string& s)
{
    std::string::size_type n = s.size();
    std::string::size_type j = 0;
    for(; j<n && s[n-j] == ' '; j++);

    std::string::size_type i = 0;
    for(; i<s.size() && s[i] == ' '; i++);
    return std::string(s.begin() + i, s.end()-j);
}

#include <fstream>
#include <iostream>
#include <sstream>
static bool isAnnotation(const std::string& line)
{
    std::regex re(R"(^\s*;.*$)");
    return std::regex_match(line, re);
}
static bool isKeyValue(const std::string line, std::string& key, std::string& val)
{
    std::regex re(R"(^\s*(\w+)\s*=\s*(.+?)\s*$)");
    std::smatch match;
    if(std::regex_match(line, match, re))
    {
        key = match[1];
        val = match[2];
        return true;
    }
    return false;
}
#include <stdio.h>
bool ConfigFile::Load(const std::string& path)
{
    std::ifstream ifs(path);
    if(!ifs.good())
    { 
        fprintf(stderr, "%s:%s", __log__().c_str(), "ifs.good error\n");
        return false;
    }
    std::string line;
    std:: unordered_map<std::string, std::string> *m = NULL;
    int nu_line = 0;
    while(!ifs.eof() && ifs.good())
    {
        getline(ifs, line);
        nu_line += 1;
        std::string section;
        /*
         * [section]
         * key = val
         * ....
         * ; 注释
         * */
        line = trim(line);
        if(line.length() == 0)
        {
            continue;
        }
        if(isSection(line, section))
        {
            // printf("[%s]\n", section.c_str());
            auto it = _map.find(section);
            if(it == _map.end())
            {
                m = new std::unordered_map<std::string, std::string>();
                _map.insert(STR_MAP::value_type(section, m));
            }
            else {
                m = it->second;
            }
            continue;
        }
        else if(isAnnotation(line))
        {
            continue;
        }
        // key = val
        std::string key, val;
        if(isKeyValue(line,key, val))
        {
            // printf("\t`%s`=`%s`\n", key.c_str(), val.c_str());
            m->insert(std::unordered_map<std::string, std::string>::value_type(key, val));
        }
        else
        {
            /*
             * 非空行
             * 非 key value
             * 非 注释
             * 非 段标记
             * */
            fprintf(stderr,  "unknow format at line: %d\n", nu_line);
            continue;
        }

    }
    ifs.close();
    return true;
}

bool ConfigFile::setPath(const std::string& path)
{
    ConfigFile& conf = instance();
    conf.clear();
    return conf.Load(path);

}

void ConfigFile::clear()
{
    auto it = _map.begin();
    while(it != _map.end())
    {
        auto m = it->second;
        auto mit = m->begin();
        while(mit != m->end())
        {
            m->erase(mit);
        }
        delete m;
        _map.erase(it);
    }
}
